package com.celeritymq.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

public class CelerityPoolImpl<T> implements CelerityPool<T> {
	private volatile boolean active = false;
	private BlockingDeque<T> pool;
	private final ThreadLocal<T> currentThread = new ThreadLocal<T>() {
		@Override
		public T initialValue() {
			return null;
		}
	};

	public CelerityPoolImpl(CelerityConstructorParameter<T> parameter, int poolSize) throws SecurityException,
			NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException,
			InvocationTargetException, InterruptedException {
		pool = new LinkedBlockingDeque<T>(poolSize);
		Class clz = parameter.getClz();
		Constructor<T>[] cons = clz.getDeclaredConstructors();
		Constructor<T> instanceConstructor = null;
		for (Constructor<T> constructor : cons) {
			Class[] parameterTypes = constructor.getParameterTypes();
			if (parameterTypeEquals(parameterTypes, parameter.getParameterTypes())) {
				instanceConstructor = constructor;
				break;
			}
		}
		for (int i = 0; i < poolSize; i++) {
			T t = instanceConstructor.newInstance(parameter.getParametetValues());
			pool.putLast(t);
		}
		active = true;
	}

	private boolean parameterTypeEquals(Class[] parameterTypes1, Class[] parameterTypes2) {
		if (parameterTypes1.length != parameterTypes2.length) {
			return false;
		}
		for (int i = 0; i < parameterTypes1.length; i++) {
			Class pt1 = parameterTypes1[i];
			Class pt2 = parameterTypes2[i];
			if (!(pt1.equals(pt2))) {
				return false;
			}
		}
		return true;
	}

	@Override
	public void close() {
		active = false;
		pool.clear();
		pool = null;
	}

	@Override
	public T get() throws Exception {
		if (active) {
			T t = pool.pollFirst();
			currentThread.set(t);
			return t;
		}
		return null;
	}

	@Override
	public void release() {
		T t = currentThread.get();
		try {
			pool.putLast(t);
			currentThread.remove();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

}
